package com.daxue.lcm.api.grpc.utis;

import com.daxue.lcm.api.grpc.interfaces.models.ResponseMessage.ResponseBody;
import com.fasterxml.jackson.core.type.TypeReference;
import io.grpc.Status.Code;
import io.grpc.StatusRuntimeException;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.function.Predicate;

import static com.daxue.lcm.api.grpc.utis.GrpcExceptionCode.BAD_ARGUMENT;
import static com.daxue.lcm.api.grpc.utis.GrpcExceptionCode.CONFLICT;
import static com.daxue.lcm.api.grpc.utis.GrpcExceptionCode.NOT_FOUND;
import static com.daxue.lcm.api.grpc.utis.GrpcExceptionCode.WRONG_CONTENT;
import static com.daxue.lcm.api.grpc.utis.Utils.asValue;

/**
 * @author daxue0929
 * @date 2022/10/9
 */

@Slf4j
@UtilityClass
public class GrpcClientUtils {


    public static <T, R> R extractResponseBody(
        T params,
        GrpcClientFunction<T, ResponseBody> function,
        TypeReference<R> typeReference
    ) throws Exception {
        try {
//            ResponseBody body = function.andThen(GrpcException::checkResponseBodyErrorCode)
//                .withRetry(2, new GrpcTimeoutPredicate())
//                .apply(params);
            ResponseBody body = function.apply(params);
            return asValue(body.getContent(), typeReference);
        } catch (StatusRuntimeException e) {
            if (e.getStatus().getCode() == Code.DEADLINE_EXCEEDED) {
                log.error("[Grpc] Response exceed deadline after retry. Bad api performance encountered.");
                throw new Exception(e.getMessage(), e);
            }
            throw new Exception(e.getMessage(), e);
        } catch (IOException e) {
            throw new Exception(e.getMessage(), e);
        }
    }

    public static <T, R> R extractResponseBody(
        T params,
        GrpcClientFunction<T, ResponseBody> function,
        Class<R> clazz
    ) throws Exception {
        return extractResponseBody(params, function, new TypeReference<R>() {
            @Override
            public Type getType() {
                return clazz;
            }
        });
    }

    public static ResponseBody checkResponseBodyErrorCode(
        ResponseBody body
    ) throws Exception {
        if (body.getErrorCode() == NOT_FOUND) {
            throw new Exception(body.getErrorMessage());
        }
        if (body.getErrorCode() == BAD_ARGUMENT) {
            throw new Exception(body.getErrorMessage());
        }
        if (body.getErrorCode() == CONFLICT) {
            throw new Exception(body.getErrorMessage());
        }
        if (body.getErrorCode() == WRONG_CONTENT) {
            throw new Exception(body.getErrorMessage());
        }
        return body;
    }

    class GrpcTimeoutPredicate implements Predicate<Throwable> {
        @Override
        public boolean test(final Throwable t) {
            if (t instanceof StatusRuntimeException &&
                ((StatusRuntimeException) t).getStatus().getCode() == Code.DEADLINE_EXCEEDED
            ) {
                log.warn("[Grpc] Response exceed deadline. Will retry.", t);
                return true;
            }
            return false;
        }
    }

}
